using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
using Pbdms.Common.SharedVariables;
using Pbdms.Common.CryptoUtils;
using Pbdms.Common;
using Pbdms.Common.DataObject;
namespace Pbdms.NetworkDealer
{
    public class AuthTcpServer
    {   

        /// <summary>
        /// the tcp listener 
        /// </summary>
        private TcpListener server;
        /// <summary>
        /// port number to be used
        /// </summary>
        private int port;
        private string currentUser;
        private SecretKeyControl secretKeyControl;
        private PublicKeysControl publicKeyControl;
        private FriendsControl friendsControl;
        private Boolean authenticate = false;
        private int total;
        public AuthTcpServer(int port, SecretKeyControl secretKeyControl, PublicKeysControl publicKeyControl, FriendsControl friendsControl)
        {
            ///create a tcp server
            this.port = port;
            this.secretKeyControl = secretKeyControl;
            this.publicKeyControl = publicKeyControl;
            this.friendsControl = friendsControl;
            server = new TcpListener(IPAddress.Any, port);
            // Start listening for client requests.
            server.Start();
           
        }
        public void Close()
        {
            server.Stop();
        }
        public AuthTcpClient Accept()
        {
            authenticate = false;           
            TcpClient tcpClient = server.AcceptTcpClient();
            
            Socket aa = tcpClient.Client;

            
            //reset the user
            currentUser = "";
            authenticate = false;
            try
            {
                startHandShaking(tcpClient);
               
                AuthTcpClient authClient = new AuthTcpClient(tcpClient, 100,currentUser,authenticate, secretKeyControl, publicKeyControl, friendsControl);// new AuthenticateTcpClient(tcpClient);
                return authClient;
            }
            catch
            {
                if (tcpClient != null)
                {
                    tcpClient.Close();
                }
            }
            
            return null;
        }
        private void startHandShaking(TcpClient client)
        {
            receiveMessageOne(client);
           
        }
        private void receiveMessageOne(TcpClient client)
        {
            SmartNetworkStream stream = new SmartNetworkStream(client);

            byte[] data = stream.Read();
            string message = System.Text.Encoding.ASCII.GetString(data);
                     
            string NaReceived="trashDecryptedNa";
           
            try
            {
                //decrypt 
                Decrypter decrypter = new Decrypter();
                string decryptMessage = decrypter.DecryptMessage(message, secretKeyControl.readPrivateKey());
                //verify who it is from
                Verifier verifier = new Verifier(publicKeyControl);
                Common.DataObject.VerifyObj verifObj = verifier.VerifyMessage(decryptMessage);
                //retrieve Na
                currentUser = verifObj.keyID;
                NaReceived = verifObj.Data;

            }
            catch
            {
                //failed to received message 1
            }
            finally
            {
                try
                {
                    //check whether it is trusted
                    Buddy buddy = friendsControl.getAFriendByKeyID(currentUser);
                    if (buddy.Trusted == true)
                    {
                        //trusted friend
                    }
                    else
                    {
                        //signal that this is unauthentication connection
                        //reset the currentr user to nothing to indicate unauthentication
                        currentUser = "";
                        NaReceived = "";
                    }

                }
                catch
                {
                    //signal that this is unauthentication connection
                    currentUser = "";
                    NaReceived = "";
                }
            }
               
            sendMessageTwo(client,NaReceived);
        }
        private void sendMessageTwo(TcpClient client,string NaReceived)
        {
            SmartNetworkStream stream = new SmartNetworkStream(client);

            
            string NbGenerated = Common.Global.RandomString();
            string stringToSign= NaReceived + "," + NbGenerated;
            string encryptedSignedMessage="trashEncryptedMessage2";
            try
            {
                Signer signer = new Signer();
                //sign Nb and produce a signature with Nb
                string signMessage = signer.SignAMessage(stringToSign, secretKeyControl.readSecretKey(), secretKeyControl.readPrivateKey());
                //encrypt the message
                Encrypter encrypter = new Encrypter();
                encryptedSignedMessage = encrypter.EncryptString(signMessage, publicKeyControl.getPublicKey(currentUser), true);
            }
            catch
            {
                //signal that this is unauthentication connection
                //it will send encryptedSignedMessage (fake)
                encryptedSignedMessage = "trashEncryptedMessage2";
            }
            finally
            {

            }
                //send message 2 
                byte[] data = System.Text.Encoding.ASCII.GetBytes(encryptedSignedMessage);
                stream.Write(data, 0, data.Length);
                receiveMessageThree(client, NbGenerated);
            
            
        }
        private void receiveMessageThree(TcpClient client,string NbGenerated)
        {
            SmartNetworkStream stream = new SmartNetworkStream(client);

            byte [] data=stream.Read();
            string message = System.Text.Encoding.ASCII.GetString(data);

            string NbReceived = "trashReceivedNb";
            try
            {
                //decrypt the message
                Decrypter decrypter = new Decrypter();
                string decryptMsg = decrypter.DecryptMessage(message, secretKeyControl.readPrivateKey());
                //verify the signature
                Verifier verifier = new Verifier(publicKeyControl);
                Common.DataObject.VerifyObj verifyObj = verifier.VerifyMessage(decryptMsg);
                //retrieve the Nb and the keyid that is used to sign the message
                currentUser = verifyObj.keyID;
                NbReceived = verifyObj.Data;

                if (verifyObj.keyID.Equals(currentUser))
                {                  
                    NbReceived = verifyObj.Data;
                }
                else
                {
                    //the received friend keyID and the current one do not match
                    throw new Exception();
                }
            }
            catch
            {
                currentUser = "";
            }
            finally
            {
                //failed authentication in the first place
                if (currentUser.Equals(""))
                {
                    authenticate = false;
                }
                else
                {
                    //if Nb sent and received are the same 
                    if (NbReceived.Equals(NbGenerated))
                    {
                        authenticate = true;
                    }
                    else
                    {
                        authenticate = false;
                    }
                }
            }
               
            



            
           
            
        }

    }
}
